package com.sam.spring.chapter4;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.junit.Assert;
import org.junit.Test;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

public class SpELTest {
	@Test
	public void helloSPEL() {
		ExpressionParser parser = new SpelExpressionParser();
		Expression expression = parser
				.parseExpression("('Hello' + ' SpEL').concat(#end)");
		EvaluationContext context = new StandardEvaluationContext();
		context.setVariable("end", "!");
		Assert.assertEquals("Hello SpEL!", expression.getValue(context));
	}

	@Test
	public void testParserContext() {
		ExpressionParser parser = new SpelExpressionParser();
		ParserContext parserContext = new ParserContext() {

			public boolean isTemplate() {
				return true;
			}

			public String getExpressionPrefix() {
				return "#{";
			}

			public String getExpressionSuffix() {
				return "}";
			}
		};
		String template = "#{'Hello '}#{'SPEL!'}";
		Expression expression = parser.parseExpression(template, parserContext);
		Assert.assertEquals("Hello SPEL!", expression.getValue());
	}

	@Test
	public void testLogicExpression() {
		// 逻辑表达式：且（and）、或(or)、非(!或NOT)
		String expression1 = "2>1 and (!true or !false)";
		ExpressionParser parser = new SpelExpressionParser();
		boolean result1 = parser.parseExpression(expression1).getValue(
				boolean.class);
		Assert.assertEquals(true, result1);

		String expression2 = "2>1 and (NOT true or NOT false)";
		boolean result2 = parser.parseExpression(expression2).getValue(
				boolean.class);
		Assert.assertEquals(true, result2);
	}

	@SuppressWarnings("unchecked")
	@Test
	public void testClassTypeExpression() {
		/**
		 * 使用“T(Type)”来表示java.lang.Class实例，“Type”必须是类全限定名，“java.lang”包除外，
		 * 即该包下的类可以不指定包名；使用类类型表达式还可以进行访问类静态方法及类静态字段。
		 */
		ExpressionParser parser = new SpelExpressionParser();
		// java.lang包类访问
		Class<String> result1 = parser.parseExpression("T(String)").getValue(
				Class.class);
		Assert.assertEquals(String.class, result1);
		// 其他包类访问
		String expression2 = "T(com.sam.spring.chapter4.SpELTest)";
		Class<String> result2 = parser.parseExpression(expression2).getValue(
				Class.class);
		Assert.assertEquals(SpELTest.class, result2);
		// 类静态字段访问
		int result3 = parser.parseExpression("T(Integer).MAX_VALUE").getValue(
				int.class);
		Assert.assertEquals(Integer.MAX_VALUE, result3);
		// 类静态方法调用
		int result4 = parser.parseExpression("T(Integer).parseInt('1')")
				.getValue(int.class);
		Assert.assertEquals(1, result4);
	}

	@Test
	public void testConstructorExpression() {
		/**
		 * 类实例化同样使用java关键字“new”，类名必须是全限定名， 但java.lang包内的类型除外，如String、Integer。
		 */
		ExpressionParser parser = new SpelExpressionParser();
		String result1 = parser.parseExpression("new String('haha')").getValue(
				String.class);
		Assert.assertEquals("haha", result1);

		Date result2 = parser.parseExpression("new java.util.Date()").getValue(
				Date.class);
		Assert.assertNotNull(result2);
	}

	@Test
	public void testVariableExpression() {
		/**
		 * 使用“#variable”来引用在EvaluationContext定义的变量；除了可以引用自定义变量，还可以使用“#root”引用根对象
		 * ，“#this”引用当前上下文对象，此处“#this”即根对象。
		 */
		ExpressionParser parser = new SpelExpressionParser();
		EvaluationContext context = new StandardEvaluationContext();
		context.setVariable("variable", "haha");
		context.setVariable("variable", "haha");
		String result1 = parser.parseExpression("#variable").getValue(context,
				String.class);
		Assert.assertEquals("haha", result1);

		context = new StandardEvaluationContext("haha");
		String result2 = parser.parseExpression("#root").getValue(context,
				String.class);
		Assert.assertEquals("haha", result2);

		String result3 = parser.parseExpression("#this").getValue(context,
				String.class);
		Assert.assertEquals("haha", result3);
	}

	@Test
	public void testFunctionExpression() throws NoSuchMethodException,
			SecurityException {
		/**
		 * 此处可以看出“registerFunction”和“setVariable”都可以注册自定义函数，但是两个方法的含义不一样，推荐使用“
		 * registerFunction”方法注册自定义函数。
		 */
		ExpressionParser parser = new SpelExpressionParser();
		StandardEvaluationContext context = new StandardEvaluationContext();
		Method parserInt = Integer.class.getDeclaredMethod("parseInt",
				String.class);
		context.registerFunction("parserInt", parserInt);
		context.setVariable("parserInt2", parserInt);
		String expression1 = "#parserInt('3') == #parserInt2('3')";
		boolean result1 = parser.parseExpression(expression1).getValue(context,
				boolean.class);
		Assert.assertEquals(true, result1);
	}

	@Test
	public void testAssignExpression() {
		ExpressionParser parser = new SpelExpressionParser();
		// 1.给root对象赋值
		EvaluationContext context = new StandardEvaluationContext("aaaa");
		String result1 = parser.parseExpression("#root='aaaaa'").getValue(
				context, String.class);
		Assert.assertEquals("aaaaa", result1);
		String result2 = parser.parseExpression("#this='aaaa'").getValue(
				context, String.class);
		Assert.assertEquals("aaaa", result2);

		// 2.给自定义变量赋值
		context.setVariable("#variable", "variable");
		String result3 = parser.parseExpression("#variable=#root").getValue(
				context, String.class);
		Assert.assertEquals("aaaa", result3);
	}

	@Test
	public void testObjectProperty() {
		ExpressionParser parser = new SpelExpressionParser();
		// 1.访问root对象属性
		Date date = new Date();
		StandardEvaluationContext context = new StandardEvaluationContext(date);
		int result1 = parser.parseExpression("Year").getValue(context,
				int.class);
		Assert.assertEquals(date.getYear(), result1);
		int result2 = parser.parseExpression("year").getValue(context,
				int.class);
		Assert.assertEquals(date.getYear(), result2);

		// 2.安全访问
		context.setRootObject(null);
		Object result3 = parser.parseExpression("#root?.year").getValue(
				context, Object.class);
		Assert.assertEquals(null, result3);

		// 3.给root对象属性赋值
		context.setRootObject(date);
		int result4 = parser.parseExpression("Year = 4").getValue(context,
				int.class);
		Assert.assertEquals(4, result4);
		parser.parseExpression("Year").setValue(context, 5);
		int result5 = parser.parseExpression("Year").getValue(context,
				int.class);
		Assert.assertEquals(5, result5);
	}

	@Test
	public void testBeanExpression() {
		/**
		 * SpEL支持使用“@”符号来引用Bean，在引用Bean时需要使用BeanResolver接口实现来查找Bean，
		 * Spring提供BeanFactoryResolver实现；
		 * 在示例中我们首先初始化了一个IoC容器，ClassPathXmlApplicationContext
		 * 实现默认会把“System.getProperties()”注册为“systemProperties”Bean，因此我们使用
		 * “@systemProperties”来引用该Bean。
		 */
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext();
		ctx.refresh();
		ExpressionParser parser = new SpelExpressionParser();
		StandardEvaluationContext context = new StandardEvaluationContext();
		context.setBeanResolver(new BeanFactoryResolver(ctx));
		Properties result1 = parser.parseExpression("@systemProperties")
				.getValue(context, Properties.class);
		Assert.assertEquals(System.getProperties(), result1);
	}

	@Test
	public void testCollectionsExpression() {
		ExpressionParser parser = new SpelExpressionParser();
		EvaluationContext context = new StandardEvaluationContext();
		// 将返回不可修改的空List
		List<Integer> result1 = parser.parseExpression("{}").getValue(
				List.class);

		// 对于字面量列表也将返回不可修改的List
		List<Integer> result2 = parser.parseExpression("{1,2,3}").getValue(
				List.class);
		Assert.assertEquals(new Integer(1), result2.get(0));
		try {
			result1.set(0, 2);
			// 不可能执行到这，对于字面量列表不可修改
			Assert.fail();
		} catch (Exception e) {
			System.out.println("throws exception...");
		}

		// 对于列表中只要有一个不是字面量表达式，将只返回原始List，
		// 不会进行不可修改处理
		String expression3 = "{{1+2,2+4},{3,4+4}}";
		List<List<Integer>> result3 = parser.parseExpression(expression3)
				.getValue(List.class);
		result3.get(0).set(0, 1);
		Assert.assertEquals(2, result3.size());

		// SpEL内联List访问
		int result4 = parser.parseExpression("{1,2,3}[0]").getValue(int.class);
		// 即list.get(0)
		Assert.assertEquals(1, result4);

		// SpEL目前支持所有集合类型的访问
		Collection<Integer> collection = new HashSet<Integer>();
		collection.add(1);
		collection.add(2);
		EvaluationContext context2 = new StandardEvaluationContext();
		context2.setVariable("collection", collection);
		int result5 = parser.parseExpression("#collection[1]").getValue(
				context2, int.class);
		// 对于任何集合类型通过Iterator来定位元素
		Assert.assertEquals(2, result5);

		// SpEL对Map字典元素访问的支持
		Map<String, Integer> map = new HashMap<String, Integer>();
		map.put("a", 1);
		EvaluationContext context3 = new StandardEvaluationContext();
		context3.setVariable("map", map);
		int result6 = parser.parseExpression("#map['a']").getValue(context3,
				int.class);
		Assert.assertEquals(1, result6);
	}

	@Test
	public void testModifyCollectionsExpression() {
		ExpressionParser parser = new SpelExpressionParser();
		EvaluationContext context1 = new StandardEvaluationContext();
		// 1.修改数组元素值
		int[] array = new int[] { 1, 2 };
		context1.setVariable("array", array);
		int result1 = parser.parseExpression("#array[1] = 3").getValue(
				context1, Integer.class);
		Assert.assertEquals(3, result1);

		// 2.修改集合值
		Collection<Integer> collection = new ArrayList<Integer>();
		collection.add(1);
		collection.add(2);
		EvaluationContext context2 = new StandardEvaluationContext();
		context2.setVariable("collection", collection);
		int result2 = parser.parseExpression("#collection[1] = 3").getValue(
				context2, int.class);
		Assert.assertEquals(3, result2);
		parser.parseExpression("#collection[1]").setValue(context2, 4);
		result2 = parser.parseExpression("#collection[1]").getValue(context2,
				int.class);
		Assert.assertEquals(4, result2);

		// 修改map元素值
		Map<String, Integer> map = new HashMap<String, Integer>();
		map.put("a", 1);
		EvaluationContext context3 = new StandardEvaluationContext();
		context3.setVariable("map", map);
		int result3 = parser.parseExpression("#map['a'] = 2").getValue(
				context3, int.class);
		Assert.assertEquals(2, result3);
	}

	@Test
	public void testCollectionSelect() {
		ExpressionParser parser = new SpelExpressionParser();
		// 1.首先准备测试数据
		Collection<Integer> collection = new ArrayList<Integer>();
		collection.add(4);
		collection.add(5);
		Map<String, Integer> map = new HashMap<String, Integer>();
		map.put("a", 1);
		map.put("b", 2);
		// 2.集合或数组测试
		EvaluationContext context1 = new StandardEvaluationContext();
		context1.setVariable("collection", collection);
		Collection<Integer> result1 = parser.parseExpression(
				"#collection.?[#this>4]").getValue(context1, Collection.class);
		Assert.assertEquals(1, result1.size());
		Assert.assertEquals(new Integer(5), result1.iterator().next());
		// 3.字典测试
		EvaluationContext context2 = new StandardEvaluationContext();
		context2.setVariable("map", map);
		Map<String, Integer> result2 = parser.parseExpression(
				"#map.?[#this.key != 'a']").getValue(context2, Map.class);
		Assert.assertEquals(1, result2.size());

		List<Integer> result3 = parser.parseExpression(
				"#map.?[key != 'a'].![value+1]").getValue(context2, List.class);
		Assert.assertEquals(new Integer(3), result3.iterator().next());
	}
}
